﻿#include <iostream>
#include <windows.h>
#include <stdio.h>
#include <cstdlib>
#include <conio.h>
#include <random>
using namespace std;

enum class Direction { Up, Down, Left,Right };//枚举一下四个方向
Direction nowDirection = Direction::Up;//默认方向为向上
int ScreenWidth = 40;//全局变量设定屏幕宽度便于后续调用
int ScreenHeight = 24;//全局变量设定屏幕高度便于后续调用
bool  GameGoing = true;//游戏是否还在进行？ 用于判定蛇是否死亡
int score = 0;//分数

int randint_range(int n, int m)//产生n~m间的随机数（包括m和n）
{
	double base = ((double)rand()) / RAND_MAX;
	int res = n + (base * (double)(m - n + 1));
	return res;
}

void HideCursor()//让光标影藏的函数
{
    CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}

void mousePointerGotoXY(int x, int y)//移动光标到x,y位置
{
	HANDLE hout;
	COORD pos;
	pos.X = x;
	pos.Y = y;
	hout = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleCursorPosition(hout, pos);
}


void SetConsoleWindowSize(SHORT width, SHORT height)//把窗体修改成对应尺寸
{
	HANDLE hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	SMALL_RECT wrt = { 0, 0, width - 1, height - 1 };
	SetConsoleWindowInfo(hStdOutput, TRUE, &wrt); // 设置窗体尺寸
	COORD coord = { width, height };
	SetConsoleScreenBufferSize(hStdOutput, coord); // 设置缓冲尺寸
}

class SnakeHeadCube {
	public:
		int PosX = ScreenWidth / 2;
		int PosY = ScreenHeight / 2;
		void DrawCube()//绘制本体的方法
		{
			mousePointerGotoXY(PosX, PosY);//跳转鼠标
			cout << "■";//绘制本体
		}

};

class SnakeBodyCube {
public:
	int PosX;
	int PosY;
	void DrawCube()//绘制本体的方法
	{
		mousePointerGotoXY(PosX, PosY);//跳转鼠标
		cout << "■";//绘制本体
	}
};

class Snake {
public:
	int length = 0;//蛇身的长度
	SnakeBodyCube bodyCubdes[100];//一个用于存放所有蛇身体方块的数组，长度足以容纳所有cubes
	SnakeHeadCube headCube;//创建了一个蛇头
	void DrawSnake()
	{
		DrawFurit();//绘制果实
		headCube.DrawCube();//画蛇头
		for (int i = 0;i<length;i++)
		{
			bodyCubdes[i].DrawCube();
		}//画蛇的身体
	}
	int FruitPosX;
	int FruitPosY;
	void CreateFruit()//只是确定新果实的坐标，不带绘制
	{
		FruitPosX =2* randint_range(1,ScreenWidth/2-1);
		FruitPosY = randint_range(1, ScreenHeight-1);
	}
	void Move()//将所有方块的坐标都修改好
	{
		int tempLastPosX;
		int tempLastPosY;
		if (length==0)//对于蛇身长度为0 的时候的特判
		{
			tempLastPosX = headCube.PosX;
			tempLastPosY = headCube.PosY;
		}
		else
		{
			tempLastPosX = bodyCubdes[length - 1].PosX;
			tempLastPosY = bodyCubdes[length - 1].PosY;//把最后一个身体方块的坐标存一下，万一吃到果实了呢？
		}
		for (int i = length - 1; i > 0; i--)
		{
			bodyCubdes[i].PosX = bodyCubdes[i - 1].PosX;
			bodyCubdes[i].PosY = bodyCubdes[i - 1].PosY;
		}//对除了第一个蛇身的所有蛇身进行处理
		bodyCubdes[0].PosX = headCube.PosX;
		bodyCubdes[0].PosY = headCube.PosY;//对第一个蛇身进行处理
		switch (nowDirection)
		{
		case Direction::Up:
			headCube.PosY -= 1;
			break;
		case Direction::Down:
			headCube.PosY += 1;
			break;
		case Direction::Right:
			headCube.PosX += 2;
			break;
		case Direction::Left:
			headCube.PosX -= 2;
			break;
		default:
			break;
		}
		if (headCube.PosX == FruitPosX && headCube.PosY == FruitPosY)//在移动后蛇头与果实重合了！
		{
			bodyCubdes[length].PosX = tempLastPosX;
			bodyCubdes[length].PosY = tempLastPosY;
			length++;
			score += 10;//加个十分好了
			system("cls");//清空屏幕
			CreateFruit();//创建一个新的果实
			DrawFurit();
			DrawSnake();
		}
		for (int i = 0; i < length; i++)
		{
			if (headCube.PosX == bodyCubdes[i].PosX && headCube.PosY == bodyCubdes[i].PosY)//蛇头与蛇某一块身体重合了！
			{
				Die();//那就去死吧！
				break;//跳出循环
			}
		}
		if (headCube.PosX<0 || headCube.PosX >=ScreenWidth)//超出宽度
		{
			Die();//那就去死吧！
		}
		if (headCube.PosY<0 || headCube.PosY>=ScreenHeight)//超出高度
		{
			Die();//那就去死吧！
		}
	}
	void Die()//死亡方案
	{
		system("cls");//清空屏幕
		cout << "结束！你的分数是"<<score<<endl;/*
		cout << "按任意键结束";*/
		GameGoing = false;//不玩了
	}
private:
	void DrawFurit()
	{
		mousePointerGotoXY(FruitPosX, FruitPosY);
		cout << "●";
	}
};
int main()
{   
	SetConsoleWindowSize(ScreenWidth, ScreenHeight);//把窗体修改成对应尺寸
	HideCursor();//隐藏光标
	//接下来非常长的一行是禁止用户更改窗口大小的语句
	SetWindowLongPtrA(GetConsoleWindow(), GWL_STYLE, GetWindowLongPtrA(GetConsoleWindow(), GWL_STYLE) & ~WS_SIZEBOX & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX);
	Snake snake;//新建了一只蛇
	snake.CreateFruit();
	snake.DrawSnake();
	system("pause");
	while (GameGoing)
	{
		system("cls");
		int keyNum;//用于存储当前键盘按下的键值
		if (_kbhit()) 
		{// 如果有按键按下，获取键值
			keyNum = _getch();
			if (keyNum == 119&& nowDirection != Direction::Down&&snake.length>0) //W
			{
				nowDirection = Direction::Up;
			}
			else if (keyNum == 119  && snake.length == 0) //W
			{
				nowDirection = Direction::Up;
			}
			else if (keyNum == 97 && nowDirection != Direction::Right && snake.length > 0)//A
			{
				nowDirection = Direction::Left;
			}
			else if (keyNum == 97 &&snake.length == 0)//A
			{
				nowDirection = Direction::Left;
			}
			else if (keyNum == 115 && nowDirection != Direction::Up && snake.length > 0)//S
			{
				nowDirection = Direction::Down;
			}
			else if (keyNum == 115 && snake.length == 0)//S
			{
				nowDirection = Direction::Down;
			}
			else if (keyNum == 100 && nowDirection != Direction::Left && snake.length > 0)//D
			{
				nowDirection = Direction::Right;
			}
			else if (keyNum == 100 && snake.length == 0)//D
			{
				nowDirection = Direction::Right;
			}
		}
		snake.Move();
		if (GameGoing)
		{
			snake.DrawSnake();
			Sleep(100);//暂停
		}
		
	}
	system("pause");
	FreeConsole();
	return 0;
}